package com.baiyue.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baiyue.entity.Charging;
import com.baiyue.entity.TableSet;
import com.baiyue.exception.GlobalException;
import com.baiyue.manager.CashierManger;
import com.baiyue.mapper.ChargingMapper;
import com.baiyue.model.CashierResult;
import com.baiyue.model.MoneyResult;
import com.baiyue.model.charging.ChargingDataRequest;
import com.baiyue.model.charging.ChargingDataResponse;
import com.baiyue.model.commodityConsumption.CommodityConsumptionDataRequest;
import com.baiyue.model.commodityConsumption.CommodityConsumptionDataResponse;
import com.baiyue.model.tableSet.TableSetDataRequest;
import com.baiyue.model.tollRules.TollRulesDataRequest;
import com.baiyue.model.tollRules.TollRulesDataResponse;
import com.baiyue.result.CodeMsg;
import com.baiyue.service.ChargingService;
import com.baiyue.service.CommodityConsumptionService;
import com.baiyue.service.TableSetService;
import com.baiyue.service.TollRulesService;
import com.baiyue.utils.BeanMapperUtil;
import com.baiyue.utils.UserHolderUtil;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 *
 * @author 伍波
 * @date 2024-05-22
 */
@Service
public class ChargingServiceImpl implements ChargingService {
    private static final Logger logger = LoggerFactory.getLogger(ChargingServiceImpl.class);
    @Autowired
    private ChargingMapper chargingMapper;

    @Autowired
    private TableSetService tableSetService;

    @Autowired
    private CashierManger cashierManger;

    @Autowired
    private TollRulesService tollRulesService;

    @Autowired
    private CommodityConsumptionService commodityConsumptionService;
    /**
     * 新增计费开始
     * @param request
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insert(ChargingDataRequest request) {
        logger.info("包间开启计费入参:{}", JSONObject.toJSONString(request));
        if (StringUtils.isEmpty(request.getRoomNumber())){
            logger.warn("包间号为空，开启计费失败");
            throw new GlobalException(CodeMsg.BIND_ERROR.fillArgs("包间号"));
        }
        String user = UserHolderUtil.getUser().getUsername();
        String uuid = getUuid()+request.getRoomNumber();
        if (Objects.isNull(request.getStartTime())){
            request.setStartTime(new Date());
        }
        if (Objects.isNull(request.getStatus())){
            request.setStatus(1);
        }
        Charging charging = BeanMapperUtil.map(request, Charging.class);
        charging.setUuid(uuid);
        charging.setOperator(user);
        chargingMapper.insert(charging);
        logger.info("开启计费成功，现在开始更新包间状态");
        TableSetDataRequest tableSetDataRequest = new TableSetDataRequest();
        tableSetDataRequest.setId(request.getTableId());
        tableSetDataRequest.setState(request.getStatus());
        tableSetDataRequest.setUuid(uuid);
        tableSetService.updateTableSet(tableSetDataRequest);
    }

    /**
     * 暂停计费
     * @param request
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pauseByUuid(ChargingDataRequest request) {
        logger.info("暂停计费:{}", JSONObject.toJSONString(request));
        request.setPauseStartTime(new Date());
        request.setStatus(2);
        updateChargingAndTableSet(request);
    }

    /**
     * 恢复计费
     * @param request
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recovery(ChargingDataRequest request) {
        logger.info("恢复计费:{}", JSONObject.toJSONString(request));
        if (request.getId() == null){
            logger.warn("id为空无法继续查询");
            throw new GlobalException(CodeMsg.BIND_ERROR.fillArgs("id"));
        }
        Charging res = chargingMapper.selectById(request.getId());
        request.setPauseEndTime(new Date());
        request.setStatus(1);
        Integer pauseMinute = cashierManger.calculateTimeDurationInMinutes(
                res.getPauseStartTime(),request.getPauseEndTime()
        );
        request.setPauseMinutes(pauseMinute);
        if (res.getPauseMinutes() != null){
            request.setPauseMinutes(res.getPauseMinutes() + pauseMinute);
        }
        updateChargingAndTableSet(request);
    }

    /**
     * 取消订单
     * @param request
     */
    @Override
    @Transactional
    public void cancelOrder(ChargingDataRequest request) {
        logger.info("取消订单入参:{}", JSONObject.toJSONString(request));

        validateRequest(request);

        Charging res = getChargingById(request.getId());

        validateChargingStatus(res);

        updateChargingRequest(request);

        updateChargingAndTableSet(request);
    }

    private void validateRequest(ChargingDataRequest request) {
        if (request.getId() == null) {
            logger.warn("id为空");
            throw new GlobalException(CodeMsg.BIND_ERROR.fillArgs("id"));
        }
    }

    private Charging getChargingById(Integer id) {
        Optional<Charging> resOpt = Optional.ofNullable(chargingMapper.selectById(id));
        return resOpt.orElseThrow(() -> {
            logger.warn("包间开单信息不存在请核实, id={}", id);
            return new GlobalException(CodeMsg.TABLE_CHARGING_EXIST);
        });
    }

    private void validateChargingStatus(Charging charging) {
        if (charging.getStatus() == 3) {
            logger.warn("包间订单状态已结束！");
            throw new GlobalException(CodeMsg.TABLE_CHARGING_CLOSE);
        }
    }

    private void updateChargingRequest(ChargingDataRequest request) {
        request.setStopTime(new Date());
        request.setStatus(3);
    }

    /**
     * 更新数据库
     * @param request
     */
    @Override
    @Transactional
    public void updateChargingAndTableSet(ChargingDataRequest request) {
        try {
            Charging charging = BeanMapperUtil.map(request, Charging.class);
            chargingMapper.updateById(charging);

            TableSetDataRequest tableSetDataRequest = new TableSetDataRequest();
            tableSetDataRequest.setId(request.getTableId());
            tableSetDataRequest.setState(request.getStatus() == 3 ? 0 : request.getStatus());
            if (request.getStatus() == 3){
                tableSetDataRequest.setUuid(null);
            }
            tableSetService.updateTableSet(tableSetDataRequest);

        } catch (Exception e) {
            logger.error("更新包间信息数据库时出错", e);
            throw new GlobalException(CodeMsg.DATABASE_ERROR);
        }
    }

    /**
     * 通过 ids 查询信息
     * @param request
     * @return
     */
    @Override
    public List<ChargingDataResponse> findByUuid(ChargingDataRequest request) {
        logger.info("通过uuids查询计费信息入参:{}", JSONObject.toJSONString(request));
        if (request.getUuidS().isEmpty()){
            logger.warn("可查询uuid为空请核查！！");
            return null;
        }
        List<Charging> list = chargingMapper.selectByUUIDs(request.getUuidS());
        if (list.isEmpty()){
            logger.warn("通过uuid 查询为空");
            return null;
        }

        return BeanMapperUtil.mapList(list, ChargingDataResponse.class);
    }

    /**
     * 计算金额
     * @param request
     * @return
     */
    @Override
    public MoneyResult getMoney(ChargingDataRequest request) {
        // 记录入参日志
        logger.info("获取包间消费金额：{}", JSONObject.toJSONString(request));

        // 校验参数
        if (Objects.isNull(request.getId())) {
            logger.warn("包间id入参为空需要核查");
            throw new GlobalException(CodeMsg.BIND_ERROR.fillArgs("id"));
        }

        // 查询包间记录
        Charging charging = chargingMapper.selectById(request.getId());
        logger.info("获取包间消费金额查询包间记录出参：{}", JSONObject.toJSONString(charging));

        // 创建 TollRulesDataRequest 并查询计费规则
        TollRulesDataRequest tollRequest = new TollRulesDataRequest();
        tollRequest.setId(charging.getTollRules());
        TollRulesDataResponse response = tollRulesService.findById(tollRequest);

        if (Objects.isNull(response)) {
            logger.warn("查询计费规则不存在请核实");
            throw new GlobalException(CodeMsg.TOLL_NOT_EXIST);
        }

        // 计算时间相关信息
        CashierResult cashierResult = calculateCashierResult(charging, response,request.getStopTime());

        // 计算消费金额
        BigDecimal consumption = computeConsumption(request.getUuid());

        // 组装结果
        MoneyResult moneyResult = assembleMoneyResult(cashierResult, response, consumption);

        // 记录出参日志
        logger.info("计算金钱: {}", JSONObject.toJSONString(moneyResult));

        return moneyResult;
    }

    private CashierResult calculateCashierResult(Charging charging, TollRulesDataResponse response,Date stopTime) {
        // 计算时间段相关信息
        CashierResult cashierResult = cashierManger.calculateTimeDuration(
                charging.getStartTime(), charging.getPauseStartTime(), charging.getStatus(), charging.getPauseMinutes(),
                stopTime
        );

        // 计算夜间服务时长
        CashierResult nightServiceTime = cashierManger.calculateNightServiceTime(
                charging.getStartTime(),stopTime, response.getNightServeStart(), response.getNightServeStop()
        );
        cashierResult.setNightHours(nightServiceTime.getNightHours());
        cashierResult.setNightMinutes(nightServiceTime.getNightMinutes());

        return cashierResult;
    }

    private BigDecimal computeConsumption(String uuid) {
        CommodityConsumptionDataRequest consumptionDataRequest = new CommodityConsumptionDataRequest();
        consumptionDataRequest.setUuid(uuid);
        List<CommodityConsumptionDataResponse> responseList = commodityConsumptionService.findByUuid(consumptionDataRequest);
        return cashierManger.computeConsumption(responseList);
    }

    private MoneyResult assembleMoneyResult(CashierResult cashierResult, TollRulesDataResponse response, BigDecimal consumption) {
        MoneyResult moneyResult = cashierManger.computeHoursAndMinutesExpenses(cashierResult, response,consumption);
        moneyResult.setConsumption(consumption);
        return moneyResult;
    }

    /**
     * 生成时间戳
     * @return
     */
    public String getUuid(){
        long timestamp = System.currentTimeMillis();
        return String.valueOf(timestamp);
    }
}
